#include "defines.h"
#include "kozos.h"
#include "netdef.h"
#include "netlib.h"
#include "ip.h"
#include "ping.h"
#include "lib.h"

static kz_msgbox_id_t ping_mbox;

typedef struct {
    uint8 IP[4];
    uint16 size;
} ping_send_param;

void ping_send(uint8 dst_IP[], uint16 size)
{
    ping_send_param *param = kz_kmalloc(sizeof(ping_send_param));
    memcpy(param->IP, dst_IP, 4);
    param->size = size;
    kz_send(ping_mbox, 's', (char *)param, 0, 0);
}

void ping_handle(uint8 *packet)
{
    ASSERT(ping_mbox != MSGBOX_NULL, "no ping_mbox");
    if (kz_send(ping_mbox, 'h', (char *)packet, 1, 5) == -1) {
        kz_kmfree(packet);
    }
}

static void send_ping_request(uint8 dst_IP[], uint16 size)
{
    static uint16 ping_id = 0;
    ping_header *phdr = (ping_header *)kz_kmalloc(sizeof(ping_header) + size);
    uint8 *p;
    uint16 i;

    phdr->icmp_type = 8;
    phdr->icmp_code = 0;
    phdr->icmp_checksum = 0;
    phdr->echo_id = ping_id++;
    phdr->echo_sequence_number = 0;
    for (i = 0, p = (uint8 *)(phdr + 1); i < size; ) {
        *p++ = i++;
    }
    phdr->icmp_checksum = ip_checksum((uint8 *)phdr, sizeof(ping_header) + size);

    ip_send_data(dst_IP, (uint8 *)phdr, sizeof(ping_header) + size);

    puts("PING ");
    print_IP(dst_IP);
    puts(" (");
    putval(size, 0);
    puts(" bytes)\n");

    kz_kmfree(phdr);
}

static void receive_ping_reply(uint8 *packet)
{
    ip_header *ihdr = (ip_header *)(packet + sizeof(ethernet_header));
    uint16 size = ihdr->ip_total_length - sizeof(ip_header) - sizeof(ping_header);

    putval(size, 0);
    puts(" bytes from ");
    print_IP(ihdr->ip_src_IP);
    puts("\n");
}

int ping_main(int argc, char *argv[])
{
    int cmd;
    char *p;

    ping_mbox = kz_getmsgbox();

    kz_chpri(12);

    while (1) {
        kz_recv(ping_mbox, &cmd, &p);

        if (cmd == 's') {
            ping_send_param *param = (ping_send_param *)p;
            send_ping_request(param->IP, param->size);
            kz_kmfree(param);

        } else if (cmd == 'h') {
            receive_ping_reply((uint8 *)p);
            kz_kmfree(p);

        } else {
            ASSERT(0, "ping: Bad cmd");
        }
    }

    return 0;
}
